﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataModel;
using System.Data.Entity;
using DataAccess.Datatypes;

namespace DataAccess
{
    public class GoodManager
    {
        private ModelContainer _data = new ModelContainer();

        //Devuelve todos los bienes de un sitio
        public IEnumerable<Good> GetGoods(int siteId)
        {
            List<Good> result = new List<Good>();
            IEnumerable<GoodSet> goods = _data.GoodSet.Where(g => g.SiteId == siteId);

            foreach (var item in goods)
            {
                Good good = new Good();
                good.Id = item.id;
                good.Name = item.Name;
                result.Add(good);
            }

            return result;
        }

        //Devuelve los n bienes mejor valorados de un sitio
        public IEnumerable<Good> GetBestGoodsHome(int siteId)
        {
            List<Good> result = new List<Good>();
            int n = _data.ConfigurationSet.First().RankedGoodsMax;
            IEnumerable<GoodSet> goods = _data.GoodSet.Where(g => g.SiteId == siteId);
            goods = goods.OrderByDescending(g => g.Likes);
            
            foreach (var item in goods.Take(n))
            {
                Good good = new Good();
                good.Id = item.id;
                good.Name = item.Name;
                good.Latitude = item.Latitude;
                good.Longitude = item.Longitude;
                good.PriceDay = item.PriceDay;
                good.Likes = item.Likes;
                result.Add(good);
            }

            return result;
        }

        //Devuelve los n bienes mejor valorados de un sitio
        public IEnumerable<Good> GetBestGoods(int siteId, int n)
        {
            List<Good> result = new List<Good>();
            SiteSet site = _data.SiteSet.Find(siteId);
            IEnumerable<GoodSet> goods = _data.GoodSet.Where(g => g.SiteId == siteId).Include(g => g.AttributeSet);
            goods = goods.OrderByDescending(g => g.Likes);
            
            foreach (var item in goods.Take(n))
            {
                Good good = new Good();
                good.Id = item.id;
                good.Name = item.Name;
                good.Latitude = item.Latitude;
                good.Longitude = item.Longitude;
                good.PriceDay = item.PriceDay;
                good.Likes = item.Likes;
                good.Url="http://www.ol4rent.com/" + site.Name + "/View/" + item.id;
                good.ImageUrl="http://www.ol4rent.com/HomeSite/GetImage?goodId=" + item.id;

               
                //item.AttributeSet = _data.AttributeSet.Where(a => a.GoodId == good.Id).ToList();
                string[] atributos;
               // item.AttributeSet.l
                if (item.AttributeSet.Count > 0)
                {
                    atributos = new string[item.AttributeSet.Count];
                    IEnumerator<AttributeSet> e = item.AttributeSet.GetEnumerator();

                    for (int i = 0; i < item.AttributeSet.Count; i++)
                    {
                        e.MoveNext();
                        atributos[i] = e.Current.Value;
                       
                    }

                }
                else 
                {
                    atributos = new string[0];
                }

                good.Attributes = atributos;
                result.Add(good);
            }

            return result;
        }

        //Devuelve los bienes de un usuario
        public IEnumerable<Good> GetUserGoods(int siteId, int userId)
        {
            List<Good> result = new List<Good>();
            IEnumerable<GoodSet> goods = _data.GoodSet.Where(g => g.SiteId == siteId && g.UserId == userId);
            
            foreach (var item in goods)
            {
                Good good = new Good();
                good.Id = item.id;
                good.Name = item.Name;
                good.PriceDay = item.PriceDay;
                good.Latitude = item.Latitude;
                good.Longitude = item.Longitude;
                good.Likes = item.Likes;
                result.Add(good);
            }

            return result;
        }

        //Devuelve el bien solicitado para editar
        public Good GetGoodEdit(int goodId, int userId, int siteId)
        {
            GoodSet item = _data.GoodSet.Find(goodId);
            if (item != null && item.UserId == userId && item.SiteId == siteId)
            {
                Good good = new Good();
                good.Id = item.id;
                good.Name = item.Name;
                good.PriceDay = item.PriceDay;
                good.Latitude = item.Latitude;
                good.Longitude = item.Longitude;

                return good;
            }

            return null;
        }

        //Devuelve el bien solicitado para cualquier usuario
        public Good GetGood(int goodId, int userId)
        {
            GoodSet item = _data.GoodSet.Find(goodId);
            UserSet user = _data.UserSet.Find(userId);
            if (item != null)
            {
                Good good = new Good();
                good.Id = item.id;
                good.Name = item.Name;
                good.PriceDay = item.PriceDay;
                good.Latitude = item.Latitude;
                good.Longitude = item.Longitude;
                good.Likes = item.Likes;
                good.UserName = item.UserSet.Name;
                if (user != null)
                {
                    good.UserLikes = user.LikeSet.Any(l => l.GoodId == goodId);
                }
                
                //Incluyo atributos
                foreach (var itemA in item.AttributeSet)
                {
                    GoodAttribute att = new GoodAttribute();
                    att.id = itemA.id;
                    att.Name = itemA.SiteAttributeSet.Name;
                    att.Value = itemA.Value;
                    good.GoodAttributes.Add(att);
                }

                //Incluyo contenidos
                foreach (var itemC in item.ContentSet)
                {
                    if (itemC.Active)
                    {
                        Content cont = new Content();
                        cont.id = itemC.id;
                        cont.Comment = itemC.Comment;
                        cont.Date = itemC.Date;
                        cont.Inappropriates = itemC.InnapropiateSet.Count;
                        cont.Url = itemC.Url;
                        cont.UserName = itemC.UserSet.Name;
                        if (user != null)
                        {
                            cont.UserInapp = user.Innapropiate.Any(i => i.ContentId == itemC.id);
                        }
                        good.Contents.Add(cont);
                    }
                }

                //Incluyo períodos de no disponibilidad
                foreach (var itemR in item.RentSet)
                {
                    NotAvailable not = new NotAvailable();
                    not.Start = itemR.Start;
                    not.Finish = itemR.Finish;
                    good.NotAvailables.Add(not);
                }

                return good;
            }
            
            return null;
        }

        //Devuelve los períodos de no disponibilidad del bien
        public IEnumerable<NotAvailable> GetGoodAvailability(int goodId)
        {
            GoodSet item = _data.GoodSet.Find(goodId);
            List<NotAvailable> list = new List<NotAvailable>();

            //Incluyo períodos de no disponibilidad
            foreach (var itemR in item.RentSet)
            {
                NotAvailable not = new NotAvailable();
                not.Start = itemR.Start;
                not.Finish = itemR.Finish;
                list.Add(not);
            }

            return list;
        }

        public ICollection<SiteAttributeSet> GetSiteAttributes(int siteId)
        {
            return _data.SiteSet.Find(siteId).SiteAttributeSet;
        }

        //Devuelve la imagen del bien soliciado
        public byte[] GetGoodImage(int goodId)
        {
            GoodSet good = _data.GoodSet.Find(goodId);
            return good.Image;
        }

        //Devuelve la imagen del bien soliciado
        public byte[] GetGoodBigImage(int goodId)
        {
            GoodSet good = _data.GoodSet.Find(goodId);
            return good.BigImage;
        }

        //Agrega un bien
        public void AddGood(int siteId, int userId, string name, byte[] image, byte[] bigImage, decimal priceDay, decimal latitude, decimal longitude)
        {
            SiteSet site = _data.SiteSet.Find(siteId);
            UserSet user = _data.UserSet.Find(userId);
            if (site != null && user != null)
            {
                GoodSet good = new GoodSet();
                good.Name = name;
                good.Image = image;
                good.BigImage = bigImage;
                good.PriceDay = priceDay;
                good.Latitude = latitude;
                good.Longitude = longitude;
                good.SiteId = site.id;
                good.SiteSet = site;
                good.UserId = user.id;
                good.UserSet = user;
                good.CreatedAt = System.DateTime.Today;
                _data.GoodSet.Add(good);
                user.GoodSet.Add(good);
                site.GoodSet.Add(good);
                _data.SaveChanges();
            }
        }
        
        //Agrega un atributo a un bien
        public void AddAttribute(int goodId, int siteAttributeId, string value)
        {
            GoodSet good = _data.GoodSet.Find(goodId);
            SiteAttributeSet siteAtt = _data.SiteAttributeSet.Find(siteAttributeId);
            if (good != null && siteAtt != null)
            {
                try
                {
                    AttributeSet att = _data.AttributeSet.Where(a => a.GoodId == goodId && a.SiteAttributeId == siteAttributeId).First();
                    att.Value = value;
                }
                catch
                {
                    AttributeSet attribute = new AttributeSet();
                    attribute.Value = value;
                    attribute.GoodId = good.id;
                    attribute.GoodSet = good;
                    attribute.SiteAttributeId = siteAtt.id;
                    attribute.SiteAttributeSet = siteAtt;
                    good.AttributeSet.Add(attribute);
                }
                _data.SaveChanges();
            }
        }

        //Agrega un contenido a un bien
        public void AddContent(int goodId, string url, string comment, System.DateTime date, int userId)
        {
            GoodSet good = _data.GoodSet.Find(goodId);
            UserSet user = _data.UserSet.Find(userId);
            if (good != null && user != null)
            {
                ContentSet content = new ContentSet();
                content.Url = url;
                content.Comment = comment;
                content.Date = date;
                content.UserId = userId;
                content.UserSet = user;
                user.ContentSet.Add(content);
                content.GoodId = goodId;
                content.GoodSet = good;
                good.ContentSet.Add(content);
                content.Active = true;
                _data.SaveChanges();
            }
        }
        
        //Agrega un like a un bien
        public void AddLike(int goodId, int userId)
        {
            GoodSet good = _data.GoodSet.Find(goodId);
            UserSet user = _data.UserSet.Find(userId);
            if (good != null && user != null)
            {
                LikeSet like = new LikeSet();
                like.GoodSet = good;
                like.GoodId = goodId;
                like.UserId = userId;
                user.LikeSet.Add(like);
                good.LikeSet.Add(like);
                good.Likes++;
                _data.SaveChanges();
            }
        }

        //Remueve un like a un bien
        public void RemoveLike(int goodId, int userId)
        {
            GoodSet good = _data.GoodSet.Find(goodId);
            UserSet user = _data.UserSet.Find(userId);
            LikeSet like = _data.LikeSet.Where(l => l.GoodId == goodId && l.UserId == userId).First();
            if (like != null && user != null && good != null)
            {
                user.LikeSet.Remove(like);
                good.LikeSet.Remove(like);
                good.Likes--;
                _data.LikeSet.Remove(like);
                _data.SaveChanges();
            }
        }

        //Agrega una marca de inapropiado a un contenido
        public void AddInnapropiate(int contentId, int userId)
        {
            ContentSet content = _data.ContentSet.Find(contentId);
            UserSet user = _data.UserSet.Find(userId);
            if (content != null && user != null)
            {
                InnapropiateSet inn = new InnapropiateSet();
                inn.UserId = userId;
                inn.UserSet = user;
                user.Innapropiate.Add(inn);
                inn.ContentId = contentId;
                inn.ContentSet = content;
                content.InnapropiateSet.Add(inn);
                _data.SaveChanges();
            }
        }

        //Modifica un bien
        public void ModifyGood(int id, string name, byte[] image, byte[] bigImage, decimal priceDay, decimal latitude, decimal longitude)
        {
            GoodSet good = _data.GoodSet.Find(id);
            if (good != null)
            {
                good.Name = name;
                good.Image = image;
                good.BigImage = bigImage;
                good.PriceDay = priceDay;
                good.Latitude = latitude;
                good.Longitude = longitude;
                if (image != null)
                {
                    good.Image = image;
                }
                if (bigImage != null)
                {
                    good.BigImage = bigImage;
                }
                _data.SaveChanges();
            }
        }

        //Realiza una busqueda de un bien segun los criterios especificados
        public IEnumerable<Good> SearchGoods(int siteId, string criterion, DateTime start, DateTime finish)
        {
            List<Good> result = new List<Good>();
            if (start.CompareTo(finish) <= 0)
            {
                IEnumerable<GoodSet> goods = _data.GoodSet.Where(g => g.SiteId == siteId).Include(g => g.AttributeSet).Include(g => g.RentSet);
                goods.OrderByDescending(g => g.Likes);

                foreach (var item in goods)
                {
                    Good good = new Good();
                    good.Id = item.id;
                    good.Name = item.Name;
                    good.Latitude = item.Latitude;
                    good.Longitude = item.Longitude;
                    good.PriceDay = item.PriceDay;
                    good.Likes = item.Likes;
                    if (CriterionMatch(item, criterion, start, finish))
                    {
                        result.Add(good);
                    }
                }
            }
            
            return result;
        }

        //Agrega un periodo de no disponibilidad al bien
        public void SetNotAvailable(int goodId, DateTime start, DateTime finish)
        {
            GoodSet good = _data.GoodSet.Find(goodId);
            if (good != null)
            {
                NotAvailableSet nas = new NotAvailableSet();
                nas.Start = start;
                nas.Finish = finish;
                nas.GoodId = goodId;
                nas.GoodSet = good;
                good.NotAvailableSet.Add(nas);
                _data.SaveChanges();
            }
        }

        //Agrega un periodo de renta al bien, pendiente de confirmacion
        public Good RentGood(int userId, int goodId, DateTime start, DateTime finish)
        {
            GoodSet item = _data.GoodSet.Find(goodId);
            UserSet user = _data.UserSet.Find(userId);
            Good good = null;
            if (item != null && user != null && IsAvailable(item, start, finish))
            {
                good = new Good();
                string token = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace('+','x').Replace('/','x').Replace('=','x');
                decimal price = CalculatePrice(item.PriceDay, start, finish);
                //Almaceno el periodo de renta
                RentSet rent = new RentSet();
                rent.Start = start;
                rent.Finish = finish;
                rent.Price = price;
                rent.Token = token;
                rent.Active = false;
                rent.GoodId = goodId;
                rent.GoodSet = item;
                item.RentSet.Add(rent);
                rent.UserId = userId;
                rent.UserSet = user;
                user.RentSet.Add(rent);
                _data.SaveChanges();
                //Datos del bien
                good.Id = item.id;
                good.Name = item.Name;
                good.PriceDay = price;
                good.Url = token;
            }

            return good;
        }

        //Confirma la renta de un bien
        public void RentConfirmation(string token)
        {
            try
            {
                RentSet rent = _data.RentSetSet.Where(r => r.Token == token).First();
                rent.Active = true;
                _data.SaveChanges();
            }
            catch { }
        }

        //Calcula el precio de un bien para el rango de fechas dado
        public decimal CalculatePrice(decimal price, DateTime start, DateTime finish)
        {
            int days;
            if (finish.Year == start.Year)
            {
                days = finish.DayOfYear - start.DayOfYear;
            }
            else
            {
                days = finish.DayOfYear + 365 - start.DayOfYear;
            }
            return days * price;
        }

        //Devuelve true si el bien esta disponible entre las fechas especificadas
        public bool IsAvailable(GoodSet good, DateTime start, DateTime finish)
        {
            //Si start es mayor o igual que finish o menor que la fecha de hoy, devuelvo error
            if (start.CompareTo(finish) >= 0 || start.CompareTo(DateTime.Today) <= 0)
            {
                return false;
            }
            //Reviso que el rango pasado no este dentro de ningun rango en los que se alquilo el bien
            foreach (var item in good.RentSet)
            {
                if ((start.CompareTo(item.Start) >= 0 && start.CompareTo(item.Finish) <= 0) ||
                    (finish.CompareTo(item.Start) >= 0 && finish.CompareTo(item.Finish) <= 0))
                {
                    return false;
                }
            }

            return true;
        }

        //Devuelve true si el bien cumple los criterios de busqueda
        public bool CriterionMatch(GoodSet good, string criterion, DateTime start, DateTime finish)
        {
            foreach (var item in good.RentSet)
            {
                if ((start.CompareTo(item.Start) >= 0 && start.CompareTo(item.Finish) <= 0) ||
                    (finish.CompareTo(item.Start) >= 0 && finish.CompareTo(item.Finish) <= 0))
                {
                    return false;
                }
            }
            if (criterion == null)
            {
                return true;
            }
            else
            {
                string crit = criterion.ToLower();
                if (good.Name.ToLower().Contains(crit))
                {
                    return true;
                }

                foreach (var item in good.AttributeSet)
                {
                    if (item.Value.ToLower().Contains(crit))
                    {
                        return true;
                    }
                }
            }
            
            return false;
        }

    }
}
